package org.example.netty.httpserver;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.internal.SystemPropertyUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.netty.handler.FlowMonitoringHandler;

@Slf4j
public class HttpEchoServer {

    private static final Integer THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

    public static void main(String[] args) {

        boolean isEpollAvailable = Epoll.isAvailable();
        log.info("isEpollAvailable = {}", isEpollAvailable);

        ServerBootstrap bootstrap = new ServerBootstrap();
        DefaultThreadFactory bossThreadFactory = new DefaultThreadFactory("server-acceptor");
        DefaultThreadFactory workerThreadFactory = new DefaultThreadFactory("server-worker");

        EventLoopGroup bossGroup = isEpollAvailable ? new EpollEventLoopGroup(THREADS, bossThreadFactory) : new NioEventLoopGroup(THREADS, bossThreadFactory);
        EventLoopGroup workerGroup = isEpollAvailable ? new EpollEventLoopGroup(THREADS, workerThreadFactory) : new NioEventLoopGroup(THREADS, workerThreadFactory);
        bootstrap.group(bossGroup, workerGroup);

        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.option(ChannelOption.SO_RCVBUF, 1024);
        bootstrap.channel(isEpollAvailable ? EpollServerSocketChannel.class : NioServerSocketChannel.class);
        bootstrap.handler(new LoggingHandler(LogLevel.INFO));

        NioEventLoopGroup shapingHandlerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("TS"));

        // 所有实例共享
        FlowMonitoringHandler monitoringHandler = new FlowMonitoringHandler(shapingHandlerGroup, 10 * 1024, 10 * 1024);

        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                // 设置限流，每秒10k
                ch.pipeline().addLast(monitoringHandler);
                // 5秒钟的空闲监测
                ch.pipeline().addLast("idle-handle", new IdleStateHandler(5, 0, 0));
                ch.pipeline().addLast(new ByteBufEchoServer());

                // http的相关处理
                ch.pipeline().addLast("server-codec", new HttpServerCodec());
                ch.pipeline().addLast(new HttpObjectAggregator(65535));
                ch.pipeline().addLast(new HttpContentCompressor());
                ch.pipeline().addLast("echo-handler", new HttpEchoHandler());
//                ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
//                ch.pipeline().addLast("metrics", new MetricsHandler());
                ch.pipeline().addLast("flush-optimized", new FlushOptimizedOrderHandler());
            }
        });

        try {
            final Channel channel = bootstrap.bind(8080).sync().channel();
            log.info("服务启动成功");
            channel.closeFuture().sync();
            log.info("服务关闭成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
